﻿#include "as_connector.h"
#include "epoll_watch.h"
#include "as_address.h"
#include "as_socket.h"
#include "as_channel.h"
#include "cross_comm.h"
#include "log.h"
#include <iostream>
#include <string>
#ifndef _WIN32
int strerror_s(char *msg, int err)
{
	int len = strlen(msg);
	strerror_r(err, msg, len);
	return len;
}
#endif

enum CSTATE : uint8
{
	NONE,
	CONNECTING,
	CONNECTED,
	DISCONNECT,
};

as::net::Connector::Connector(std::shared_ptr<EpollWatch> &ep, Address &addr)
	:ep_watch_(ep), server_addr_(addr), socket_(-1)
{
	state_ = NONE;
}

void as::net::Connector::start()
{
	connect_ = true;
	connect();
}

void as::net::Connector::stop()
{
	connect_ = false;
}

int as::net::Connector::connect()
{
	socket_fd fd = Socket::create_socket(server_addr_.GetAF());
	std::shared_ptr<Socket> socket = std::make_shared<Socket>(fd);
	// socket->set_non_block();
	int result = socket->connect(server_addr_);
	int err = result == 0 ? 0 : socket->get_last_err();
	switch (err)
	{
	case 0:
	{
		connection(socket);
		break;
	}
	case EINPROGRESS:
	{
		fd_set writeSet;
		FD_ZERO(&writeSet);
		FD_SET(fd, &writeSet);

		timeval timeout;
		timeout.tv_sec = 5;  // 设置超时时间为5秒
		timeout.tv_usec = 0;

		result = select(fd + 1, NULL, &writeSet, NULL, &timeout);
		if (result > 0) {
			// 套接字可写，连接已完成
			connection(socket);
		}
		else if (result == 0) {
			// 连接超时
			LOG_WARNING << "Connection timeout";
		}
		else {
			// 连接错误
			char errmsg[25] = "\0";
			strerror_s(errmsg, errno);
			LOG_ERROR << "Failed to connect: " << errmsg;
		}
		break;
	}
	case EAGAIN:
	case EADDRINUSE:
	case EADDRNOTAVAIL:
	case ECONNREFUSED:
	case ENETUNREACH:
	case EINVAL:
	{
		retry(socket);
		break;
	}
#ifdef _WIN32
	case WSAECONNREFUSED:
	{
		retry(socket);
		break;
	}
	case WSAEADDRNOTAVAIL:
	{
		retry(socket);
		break;
	}
#endif
	default:
	{
		state_ = DISCONNECT;
		LOG_ERROR << "connector error:" << err;
		break;
	}
	}
	return result;
}

void as::net::Connector::reconnect()
{
	state_ = DISCONNECT;
	connect();
}

void as::net::Connector::retry(std::shared_ptr<Socket> &socket)
{
	if (reconnect_time_ <= 0)
		return;

	LOG_DEBUG << "start reconnect:" << socket->get_fd();
	socket->close();
	state_ = DISCONNECT;
	if (ep_watch_.expired() == false)
	{
		auto ep = ep_watch_.lock();
		auto self = this->shared_from_this();
		ep->AddTimer([self]()
			{
				LOG_DEBUG << "do reconnect";
				self->connect();
			}
		, reconnect_time_);
	}
}

void as::net::Connector::shutdown()
{
	state_ = DISCONNECT;
	socket_.shutdown(AS_SD_BOTH);
}

void as::net::Connector::SetNewConnCb(NewConnectCb cb)
{
	new_conn_cb_ = cb;
}

void as::net::Connector::SetReConnectTime(std::time_t time)
{
	reconnect_time_ = time;
}

void as::net::Connector::connection(std::shared_ptr<Socket> &socket)
{
	if (ep_watch_.expired() == false)
	{
		state_ = CONNECTING;
		channel_.reset();
		auto ep = ep_watch_.lock();
		channel_ = std::make_shared<Channel>(ep, socket);
		channel_->SetWriteCallback(std::bind(&Connector::HandleWrite, this));
		channel_->SetErrorCallback(std::bind(&Connector::HandleError, this));
		channel_->EnableReadAndWriteFlag();
	}
}

void as::net::Connector::HandleWrite()
{
	//正在连接
	if (state_ == CONNECTING)
	{
		channel_->ClearFlag();
		channel_->RemoveLoop();
		auto socket = channel_->GetSocket();
		int ret = Socket::get_last_err(socket->get_fd());
		if (ret == -1)
		{
			retry(socket);
		}
		else
		{
			LOG_DEBUG << "connector output ret:" << ret;
			state_ = CONNECTED;
			if (new_conn_cb_)
			{
				new_conn_cb_(channel_->GetFd());
			}
		}
	}
	else
	{
		LOG_ERROR << "connector error state:" << state_;
	}
}

void as::net::Connector::HandleError()
{
	auto socket = channel_->GetSocket();
	socket->close();
	state_ = DISCONNECT;
	retry(socket);
}

